package manager

import (
	"barge/pkg/uix"
	"fmt"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/rs/zerolog/log"
	"strconv"
	"strings"
)

type Config struct {
	Rule        map[string]*Rule     `toml:"rule"`
	Center      map[string]*Center   `toml:"center"`
	Registry    map[string]*Registry `toml:"registry"`
	Rules       []*Rule              `toml:"rules"`
	Centers     []*Center            `toml:"centers"`
	Registries  []*Registry          `toml:"registries"`
	ruleMap     map[string]*Rule
	centerMap   map[string]*Center
	RegistryMap map[string]*Registry
}

func (c *Config) GetRuleMap() map[string]*Rule {
	if c.ruleMap == nil {
		c.ruleMap = make(map[string]*Rule)
		for _, rule := range c.Rules {
			c.ruleMap[rule.Name] = rule
		}
	}
	return c.ruleMap
}

func (c *Config) GetCenterMap() map[string]*Center {
	if c.centerMap == nil {
		c.centerMap = make(map[string]*Center)
		for _, center := range c.Centers {
			c.centerMap[center.Name] = center
		}
	}
	return c.centerMap
}

func (c *Config) GetRegisterMap() map[string]*Registry {
	if c.RegistryMap == nil {
		c.RegistryMap = make(map[string]*Registry)
		for _, registry := range c.Registries {
			c.RegistryMap[registry.Name] = registry
		}
	}
	return c.RegistryMap
}

func (c *Config) Tidy() {
	c.TidyRules()
	c.TidyCenters()
	c.TidyRegistries()
}

func (c *Config) TidyRules() {
	if len(c.Rule) > 0 {
		for name, rule := range c.Rule {
			rule.Name = name
			c.Rules = append(c.Rules, rule)
		}
		c.Rule = nil
	}
	for _, rule := range c.Rules {
		rule.Tidy()
	}
	slice.SortBy(c.Rules, func(a, b *Rule) bool {
		return a.Name < b.Name
	})
}

func (c *Config) TidyCenters() {
	if len(c.Center) > 0 {
		for name, center := range c.Center {
			center.Name = name
			c.Centers = append(c.Centers, center)
		}
		c.Center = nil
	}
	for _, center := range c.Centers {
		center.Tidy()
		center.Password.Action(c.Rules)
		err := center.Password.EncodeAes()
		if err == nil {
			center.Password.Raw = ""
		}
	}
	slice.SortBy(c.Centers, func(a, b *Center) bool {
		return a.Name < b.Name
	})
}

func (c *Config) TidyRegistries() {
	if len(c.Registry) > 0 {
		for name, registry := range c.Registry {
			registry.Name = name
			c.Registries = append(c.Registries, registry)
		}
		c.Registry = nil
	}
	for _, registry := range c.Registries {
		registry.Tidy()
		registry.Password.Action(c.Rules)
		err := registry.Password.EncodeAes()
		if err == nil {
			registry.Password.Raw = ""
		}
	}
	slice.SortBy(c.Registries, func(a, b *Registry) bool {
		return a.Name < b.Name
	})
}

func (c *Config) Export() {
	for _, center := range c.Centers {
		err := center.Password.DecodeAes()
		if err == nil {
			center.Password.Aes = ""
		}
	}
	for _, registry := range c.Registries {
		err := registry.Password.DecodeAes()
		if err == nil {
			registry.Password.Aes = ""
		}
	}
}

func (c *Config) ListRule() {
	data := make([][]string, 0, len(c.Rules)+1)
	data = append(data, []string{"Reference", "Action", "Type", "Source", "Target", "Discript"})
	for _, rule := range c.Rules {
		data = append(data, []string{rule.Name, rule.Action, rule.Type, rule.Source, rule.Target, rule.Comment})
	}
	uix.Table(data)
}

func (c *Config) ListCenter() {
	data := make([][]string, 0, len(c.Centers)+1)
	data = append(data, []string{"Reference", "Host", "Port", "Username", "Password"})
	for _, center := range c.Centers {
		err := center.Password.DecodeAes()
		if err != nil {
			log.Warn().Err(err).Str("name", center.Name).Msg("decode center password failed")
			center.Password.Raw = "******"
		} else {
			center.Password.Action(c.Rules)
		}
		data = append(data, []string{center.Name, center.Host, strconv.Itoa(center.Port), center.Username, center.Password.Raw})
	}
	uix.Table(data)
}

func (c *Config) ListRegistry() {
	data := make([][]string, 0, len(c.Registries)+1)
	data = append(data, []string{"Reference", "address", "Username", "Password", "Arch"})
	for _, registry := range c.Registries {
		err := registry.Password.DecodeAes()
		if err != nil {
			log.Warn().Err(err).Str("name", registry.Name).Msg("decode registry password failed")
			registry.Password.Raw = "******"
		} else {
			registry.Password.Action(c.Rules)
		}
		data = append(data,
			[]string{registry.Name, registry.Address, registry.Username, registry.Password.Raw, strings.Join(registry.Architectures, ",")},
		)
	}
	uix.Table(data)
}

func (c *Config) SetRule(o *Rule) {
	for _, rule := range c.Rules {
		if rule.Name == o.Name {
			*rule = *o
			return
		}
	}
	c.Rules = append(c.Rules, o)
}

func (c *Config) SetCenter(o *Center) {
	o.Password.Action(c.Rules)
	err := o.Password.EncodeAes()
	if err == nil {
		o.Password.Raw = ""
	}
	for _, center := range c.Centers {
		if center.Name == o.Name {
			*center = *o
			return
		}
	}
	c.Centers = append(c.Centers, o)
}

func (c *Config) SetRegistry(o *Registry) {
	o.Password.Action(c.Rules)
	err := o.Password.EncodeAes()
	if err == nil {
		o.Password.Raw = ""
	}
	for _, registry := range c.Registries {
		if registry.Name == o.Name {
			*registry = *o
			return
		}
	}
	c.Registries = append(c.Registries, o)
}

func (c *Config) DeleteRule(name string) {
	fmt.Println(name)
	for i := range c.Rules {
		if c.Rules[i].Name == name {
			c.Rules = append(c.Rules[:i], c.Rules[i+1:]...)
			return
		}
	}
	//delete(c.Rules, name)
}

func (c *Config) DeleteCenter(name string) {
	if c.Centers == nil {
		return
	}
	for i := range c.Centers {
		if c.Centers[i].Name == name {
			c.Centers = append(c.Centers[:i], c.Centers[i+1:]...)
			return
		}
	}
}

func (c *Config) DeleteRegistry(name string) {
	fmt.Println(name)
	for i := range c.Registries {
		if c.Registries[i].Name == name {
			c.Registries = append(c.Registries[:i], c.Registries[i+1:]...)
			return
		}
	}
}

func (c *Config) GetRule(name string) *Rule {
	if c.Rules == nil {
		return nil
	}
	return c.GetRuleMap()[name]
}

func (c *Config) GetCenterByName(name string) *Center {
	if c.Centers == nil {
		return nil
	}
	center := c.GetCenterMap()[name]
	if center == nil {
		return nil
	}
	center.Name = name
	err := center.Password.DecodeAes()
	if err != nil {
		log.Warn().Str("name", name).Msg("decode center password failed")
	} else {
		center.Password.Action(c.Rules)
	}
	return center
}

func (c *Config) GetRegistryByName(name string) *Registry {
	if c.Registries == nil {
		return nil
	}
	registry := c.GetRegisterMap()[name]
	if registry == nil {
		return nil
	}
	registry.Name = name
	err := registry.Password.DecodeAes()
	if err != nil {
		log.Warn().Str("name", name).Msg("decode registry password failed")
	} else {
		registry.Password.Action(c.Rules)
	}
	return registry
}
